#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../util.h"
#include "../errormsg.h"
#include "../symbol.h"
#include "temp.h"
#include "tree.h"
#include "frame.h"


#define SIZE_EDGE 4
const int WORD_SIZE = 4;



F_access InFrame(int offset)
{
	F_access p = checked_malloc(sizeof *p);
	p->kind = inFrame;
	p->u.offset = offset;
	return p;
}

F_access InReg(Temp_temp reg)
{
	F_access p = checked_malloc(sizeof *p);
	p->kind = inReg;
	p->u.reg = reg;
	return p;
}

F_accessList F_AccessList(F_access head, F_accessList tail)
{
	F_accessList list = checked_malloc(sizeof (*list));
	list->head = head;
	list->tail = tail;
	return list;
}



Temp_label F_name(F_frame f)
{
	return f->name;
}

F_accessList F_Formals(F_frame f)
{
	return f->formals;
}

F_access F_allocLocal(F_frame f, bool escape)
{
	F_access acs;

	if(escape){
		f->local_count++;
		// -2 is fixed as MIPS needs $ra and $fp be stated
		acs = InFrame(WORD_SIZE * (-2 - f->local_count));
	}
	else acs = InReg(Temp_newtemp());
	if(f->locals){
		F_accessList list = f->locals;
		while (list->tail) list = list->tail;
		list->tail = F_AccessList(acs, NULL);
	}
	else f->locals = F_AccessList(acs, NULL);

	return acs;
}

int F_getoffset(F_access acs)
{
	EM_debug(6, "frame get offset");
	assert(acs && acs->kind == inFrame);
	return acs->u.offset;
}

// fragments

F_frag F_StringFrag(Temp_label label, string str)
{
	F_frag p = checked_malloc(sizeof(*p));
	p->kind = F_stringFrag;
	p->u.stringg.label = label;
	p->u.stringg.str = str;
	return p; 
}

F_frag F_ProcFrag(T_stm stm, F_frame frame)
{
	F_frag p = checked_malloc(sizeof(*p));
	p->kind = F_procFrag;
	p->u.proc.stmbody = stm;
	p->u.proc.frame = frame;
	return p;
}

F_fragList F_FragList(F_frag head, F_fragList tail)
{
	F_fragList p = checked_malloc(sizeof(*p));
	p->head = head;
	p->tail = tail;
	return p;
}

Temp_temp F_FP(void)
{
	static Temp_temp _fp = NULL;

	if(!_fp) _fp = Temp_newtemp();
	return _fp;
}

Temp_temp F_RV(void)
{
	static Temp_temp _rv = NULL;

	if(!_rv) _rv = Temp_newtemp();
	return _rv;
}

T_exp F_Exp(F_access acs, T_exp framePtr)
{
	switch(acs->kind){
		case inFrame:
			EM_debug(4, "F_exp__inFrame");
			return T_Mem(T_Binop(
					T_plus,
					T_Const(acs->u.offset),
					framePtr));
		case inReg:
			EM_debug(4, "F_exp__inReg");
			return T_Temp(acs->u.reg);
	}

	assert(0);
	return NULL;
}

///////////

F_frame F_newFrame(Temp_label name, F_accessList formals)
{
	F_frame p = checked_malloc(sizeof (*p));
	F_accessList formal = formals, q = NULL;
	int i = 0;

	p->name = name;
	p->locals = NULL;
	p->local_count = 0;
	for(; formal; formal = formal->tail, i++){
		F_access access;
		if(formal->head || i >= SIZE_EDGE)
			access = InFrame(i * WORD_SIZE);
		else
			access = InReg(Temp_newtemp());

		if(q){
			q->tail = F_AccessList(access, NULL);
			q = q->tail;
		}
		else{
			p->formals = q = F_AccessList(access, NULL);
		}
	}
	return p;
}

T_exp F_externalCall(string s, T_expList args)
{
	return T_Call(T_Name(Temp_namedlabel(s)), args);
}


T_stm F_procEntryExit1(F_frame frame, T_stm stm)
{
	return stm;
}

